package com.ringtheweb.common.util.crypto;

/**
 * Verify a french RIB Number (algorithme de Luhn).
 * 
 * @author ldewavri
 * 
 */
public class CheckRIB {

	/**
	* Array for conversion of RIB letters to digits.
	* <table>
	* <tr><td>A</td><td>B</td><td>C</td><td>D</td><td>E</td><td>F</td><td>G</td><td>H</td><td>I</td></tr>
	* <tr><td>J</td><td>K</td><td>L</td><td>M</td><td>N</td><td>O</td><td>P</td><td>Q</td><td>R</td></tr>
	* <tr><td></td><td>S</td><td>T</td><td>U</td><td>V</td><td>W</td><td>X</td><td>Y</td><td>Z</td></tr>
	* <tr><td>-</td><td>-</td><td>-</td><td>-</td><td>-</td><td>-</td><td>-</td><td>-</td><td>-</td></tr>
	* <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr>
	* </table>
	*/
	private static final char convertTab[][] = {{ 
	    	'A', '1' }, {
			'B', '2' }, {
			'C', '3' }, {
			'D', '4' }, {
			'E', '5' }, {
			'F', '6' }, {
			'G', '7' }, {
			'H', '8' }, {
			'I', '9' }, {
			'J', '1' }, {
			'K', '2' }, {
			'L', '3' }, {
			'M', '4' }, {
			'N', '5' }, {
			'O', '6' }, {
			'P', '7' }, {
			'Q', '8' }, {
			'R', '9' }, {
			'S', '2' }, {
			'T', '3' }, {
			'U', '4' }, {
			'V', '5' }, {
			'W', '6' }, {
			'X', '7' }, {
			'Y', '8' }, {
			'Z', '9' }
	};

	/** private constructor, this helper class should not be instanciated */
	private CheckRIB() {/* EMPTY */}

	/**
	 * Calculate a RIB key.
	 *
	 *<ul>
	 *<li>Concat everything to have a 23 digits String</li>
	 *<li>le reste de la division de ce nombre par 97 doit etre nul</li>
	 *<li>la cle rib doit etre comprise entre 01 et 97</li>
	 *<li>Pour le v�rifier, couper le nombre de 23 chiffres en 4 morceaux (pour avoir des entiers)</li>
	 *<table>
	 *<tr>
	 *<td>9999999</td>
	 *<td>9999999</td>
	 *<td>9999999</td>
	 *<td>99</td></tr>
	 *<tr>
	 *<td><- 7 -></td>
	 *<td><- 7 -></td>
	 *<td><- 7 -></td>
	 *<td><2></td></tr>
	 *<tr>
	 *<td>   A   </td>
	 *<td>   B   </td>
	 *<td>   C   </td>
	 *<td>CLE</td></tr>
	 *</table>
	 *<li>Cle = 97-(62*A+34*B+3*C) mod 97</li>
	 * </ul>
	 * 
	 * @param rib 
	 * @return la valeur de la clef de rib
	 *
	 */
	public static long calculCleRIB(String rib)
	{
        assert(rib != null): "Le parametre d'appel 'rib' est null !";
            
		/* Remplacement des lettres par des chiffres */
		for (int i = 0; i < convertTab.length; i++)
		{
			rib = rib.replace(convertTab[i][0], convertTab[i][1]);
		}

		/* Decoupage du RIB en 3 morceaux */
		long A = Long.parseLong(rib.substring(0, 7));
		long B = Long.parseLong(rib.substring(7, 14));
		long C = Long.parseLong(rib.substring(14, 21));

		long Cle = 97 - (62 * A + 34 * B + 3 * C) % 97 ;

		return Cle;
	}

	/**
	 * 
	 * Verifie la cle RIB
	 * 
	 * @param rib donne sous la forme "<No Banque (5 chiffres)><No Guichet (5 chiffres)><No Compte (11 chiffres)><Cl� de RIB (2 chiffres)>"
	 * @return true si le rib est correct
	 * @throws ParseException sur une erreur de parsing
	 */
	public static boolean verifRIB(String rib) throws ParseException
	{
        /* V�rification de la pr�sence du parametre */
        assert(rib != null): "Le parametre d'appel 'rib' est null !";
           
		try
		{
			// rib trop long ou trop court
			if (rib.length() != 23)
			{
				throw new ParseException("le RIB ne fait pas 23 caracteres");
			}

			// parse le rib
			long cleRib = Long.parseLong(rib.substring(21, 23));

			// cle rib correspond au calcul ?
			return calculCleRIB(rib) == cleRib;

		}
		catch (NumberFormatException ex)
		{
			throw new ParseException("la cle RIB contient des caracteres non numeriques", ex);
		}
		catch (StringIndexOutOfBoundsException ex)
		{
			throw new ParseException("le RIB ne fait pas 23 caracteres", ex);
		}
	}
	
}
